//
//  MFWHttpEngine.m
//  MFWSdk
//
//  Created by Song Xiaofeng on 13-7-24.
//  Copyright (c) 2013年 Mafengwo Inc. All rights reserved.
//

#import "MFWHttpEngine.h"
#import "WeakReference.h"
#import "NSMutableArray+WeakReference.h"
#import "NSMutableSet+WeakReference.h"
#import "ASIHTTPRequest.h"
#import "ASIFormDataRequest.h"
#import "ASINetworkQueue.h"


#import "MFWDataModel.h"
#import "MFWDataCenter.h"
#import "MFWHttpTask.h"
#import "MFWHttpRequest.h"
#import "MFWJson.h"

#import "MULLoginManager.h"

#import <objc/runtime.h>
#pragma mark - Category

@interface MFWHttpRequestProgress : NSObject

@property (nonatomic, assign) long long sentBytes;
@property (nonatomic, assign) long long sendTotalBytes;

@property (nonatomic, assign) long long receivedBytes;
@property (nonatomic, assign) long long receiveTotalBytes;

@end

@implementation MFWHttpRequestProgress

@end

@interface MFWHttpRequest (EngineAdditions)

@property (nonatomic, readonly) MFWHttpRequestProgress *progressData;

@end

@implementation MFWHttpRequest (EngineAdditions)

static void * MFWHttpRequest_progressData_key = nil;
- (MFWHttpRequestProgress *)progressData
{
    MFWHttpRequestProgress *progress = objc_getAssociatedObject(self, &MFWHttpRequest_progressData_key);
    if (progress == nil) {
        progress = [[MFWHttpRequestProgress alloc] init];
        objc_setAssociatedObject(self, &MFWHttpRequest_progressData_key, progress, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return progress;
}

@end

#pragma mark - MFWHttpEngine

#define kASIRequest_UserInfo_MFWRequst @"MFWRequst"

NSString *API_HOST = nil;
NSString *const MFWErrorDomain = @"MFWErrorDomain";
NSString *const kMFWErrorInfo_Str_Msg = @"kMFWErrorInfo_Str_Msg";
NSString *const kMFWErrorInfo_Int_StatusCode = @"kMFWErrorInfo_Int_StatusCode";

@interface MFWHttpEngine()
{
    NSThread *_networkThread;
    NSThread *_dataThread;
}
@end

@implementation MFWHttpEngine

+ (void)setApiHost:(NSString*)apiHost;
{
    API_HOST = [apiHost copy];
}

+ (void)setMFWHttpRequestFilter:(MFWHttpRequestFilter)requestFilter;
{
    [MFWHttpEngine sharedInstance].requestFilter = requestFilter;
}

+ (MFWHttpEngine*)sharedInstance;
{
    static MFWHttpEngine *_sharedInstance = nil;
	if (_sharedInstance == nil)
	{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _sharedInstance = [[MFWHttpEngine alloc] init];
			
        });
	}
	
	return _sharedInstance;
}

- (id)init
{
    self = [super init];
    if (self)
    {
        [self _initializeEngine];
    }
    return self;
}

- (void)_initializeEngine
{
    _weakDataObserverDict = [NSMutableDictionary dictionary];
    _weakGlobalResultHooks = [NSMutableArray array];
    
    // init network queues
    _networkQueues = [NSMutableArray arrayWithCapacity:kMFWRequestQueueCount];
    for (int i = 0; i < kMFWRequestQueueCount; i++)
    {
        //changge
        ASINetworkQueue *anASINetworkQueue = [ASINetworkQueue queue];
 
        
        [anASINetworkQueue setDelegate: self];
        [anASINetworkQueue setRequestDidStartSelector:@selector(asiRequestStarted:)];
        [anASINetworkQueue setRequestDidFinishSelector:@selector(asiRequestSuccessed:)];
        [anASINetworkQueue setRequestDidFailSelector:@selector(asiRequestFailed:)];
        [anASINetworkQueue setRequestDidReceiveResponseHeadersSelector:@selector(asiRequest:didReceiveResponseHeaders:)];
        anASINetworkQueue.shouldCancelAllRequestsOnFailure = NO;
        if (i == kMFWRequestQueueTypeFileUpload) {
            anASINetworkQueue.showAccurateProgress = YES;
        }
        
        [anASINetworkQueue go];
        
        [_networkQueues addObject:anASINetworkQueue];
    }
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}

- (BOOL)addTask:(MFWHttpTask*)task;
{
    if (task)
    {
        MFWDataModel *dataProvider = [[MFWDataCenter sharedInstance] dataModelForCategory:task.dataIdentifier.dataCategoryName];
        
        MFWHttpRequest *dataRequest = [dataProvider mfwHttpRequestForTask:task];
        
        if (!dataRequest)
        {
            return NO;
        }
        
        ASINetworkQueue *networkQueue = [self networkQueueForType:dataRequest.queueType];
        
        
        if (networkQueue == nil)
        {
            return NO;
        }
        
        NSArray *waitingRequests = [networkQueue operations];
        
        BOOL hasSameRequest = NO;
        
        for (ASIHTTPRequest *asiRequest in waitingRequests)
        {
            //TODO combine same task
            if ([asiRequest isCancelled])
            {
                continue;
            }
            
            MFWHttpRequest *waitingRequest = [asiRequest.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];

                if (dataRequest.task.dataRequestType == kMFWDataRequest_Refresh
                    && [dataRequest.task.dataIdentifier isEqual:waitingRequest.task.dataIdentifier])
                {// new request is refresh operation, if dataIdentifier is same we will cancel the older waiting request
                    [asiRequest clearDelegatesAndCancel];
                }
        }
        
            BOOL res = [self addRequest:dataRequest];
            if (res)
            {
                [self _notifyTaskAdded:task];
            }
            return res;
    }
    
    return NO;
}

- (BOOL)addRequest:(MFWHttpRequest*)mfwHttpRequest toNetworkQueue:(ASINetworkQueue*)queue
{
    if (self.requestFilter)
    {
        _requestFilter(mfwHttpRequest);
    }
    
    if (queue && mfwHttpRequest)
    {
        NSMutableDictionary *argsDic = [[NSMutableDictionary alloc] initWithDictionary:mfwHttpRequest.requestParams];
        
        MFWSdkEnvironment *environment = [MFWSdkEnvironment sharedInstance];
        
        NSString *accessToken = nil;
        NSString *tokenSecret = nil;
        
        [MULLoginManager getSignatureToken:&accessToken andSecret:&tokenSecret];
        
        if ([argsDic hasValueForKey:@"x_auth_username"])
        {
            accessToken = nil;
            tokenSecret = nil;
        }
        
        NSMutableDictionary *signedArgsDic =
                    [MFWXAuthUtils signatureAuthParams:argsDic
                                            requestURL:mfwHttpRequest.requestUrl
                                          requesMethod:mfwHttpRequest.httpMethodStr
                                   withAuthConsumerKey:environment.consumerKey
                                        consumerSecret:environment.consumerSecret
                                           accessToken:accessToken
                                           tokenSecret:tokenSecret];
        ASIHTTPRequest *asiHttpRequest = nil;
        if (mfwHttpRequest.httpMethod == kRequestHttpMethodGet
            || mfwHttpRequest.httpMethod == kRequestHttpMethodDelete)
        {
            asiHttpRequest = [self asiNormalRequest:mfwHttpRequest withArgs:signedArgsDic];
        }
        else
        {
            asiHttpRequest = [self asiFormDataRequest:mfwHttpRequest withArgs:signedArgsDic];
        }
        
        MFWHttpTask *task = mfwHttpRequest.task;
        asiHttpRequest.showAccurateProgress = NO;
        if (task)
        {
            if (task.downloadProgressDelegate || task.uploadProgressDelegate)
            {
                asiHttpRequest.downloadProgressDelegate = task.downloadProgressDelegate;
                asiHttpRequest.uploadProgressDelegate = task.uploadProgressDelegate;
                asiHttpRequest.showAccurateProgress = YES;
            }
        }
        else if (mfwHttpRequest.queueType == kMFWRequestQueueTypeFileUpload)
        {
            if (mfwHttpRequest.requestSendProgressCallback) {
                asiHttpRequest.uploadProgressDelegate = self;
                asiHttpRequest.showAccurateProgress = YES;
            }
            
            if (mfwHttpRequest.requestDownloadProgressCallBack) {
                asiHttpRequest.downloadProgressDelegate = self;
                asiHttpRequest.showAccurateProgress = YES;
            }
        }
        
        [queue addOperation:asiHttpRequest];
        
        return YES;
    }
    return NO;
}

- (BOOL)addRequest:(MFWHttpRequest*)mfwHttpRequest
{
    ASINetworkQueue *networkQueue = [self networkQueueForType:mfwHttpRequest.queueType];

    return [self addRequest:mfwHttpRequest toNetworkQueue:networkQueue];
}


/**
 *	add weak data observer, observer will be referenced weakly
 *
 *	@param	anObserver	data observer
 *	@param	category	data category
 */
- (void)addDataObserver:(id<MFWDataObserver>)anObserver forDataCategory:(NSString*)category;
{
    NSMutableArray *observersOnCategory = [ _weakDataObserverDict objectForKey:category];
    if (observersOnCategory == nil)
    {
        observersOnCategory = [NSMutableArray array];
        [_weakDataObserverDict setObject:observersOnCategory forKey:category];
    }
    
    if (![observersOnCategory containsWeakObject:anObserver])
    {
        [observersOnCategory addWeakObject:anObserver];
    }
}

- (void)_checkAndCleanUnusedCategoryData
{
    
}

/**
 *	remove data observer which obsering on the category
 *
 *	@param	anObserver	data observer
 *	@param	category	data category
 */
- (void)removeDataObserver:(id<MFWDataObserver>)anObserver forDataCategory:(NSString*)category;
{
    NSMutableArray *observersOnCategory = [_weakDataObserverDict objectForKey:category];
    if (observersOnCategory && [observersOnCategory isKindOfClass:[NSMutableArray class]])
    {
        [observersOnCategory removeWeakObject:anObserver];
    }
    [self _checkAndCleanUnusedCategoryData];
}

/**
 *	remove data oberver which obsering on all category
 *
 *	@param	anObserver	data observer
 */
- (void)removeDataObserver:(id<MFWDataObserver>)anObserver;
{
    NSArray *observersForAllCategory = [_weakDataObserverDict allValues];
    
    [observersForAllCategory enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [(NSMutableArray*)obj removeWeakObject:anObserver];
    }];
    
    [self _checkAndCleanUnusedCategoryData];
}

/**
 *	add global http result hook. This hook will not be retained.
 *
 *	@param	anResultHook	global hook
 */
- (void)addGlobalHttpResultHook:(id<MFWGlobalHttpResultHook>)anResultHook;
{
    [_weakGlobalResultHooks addWeakObject:anResultHook];
}

- (void)removeGlobalHttpResultHook:(id<MFWGlobalHttpResultHook>)anResultHook;
{
    [_weakGlobalResultHooks removeWeakObject:anResultHook];
}

- (ASINetworkQueue*)networkQueueForType:(MFWRequestQueueType)queueType
{
    if (queueType < [_networkQueues count])
    {
        return [_networkQueues objectAtIndex:queueType];
    }
    else
    {
        return nil;
    }
}

- (void)cancelHttpQueue:(MFWRequestQueueType)queuetype;
{
    [[self networkQueueForType:queuetype] cancelAllOperations];
}

- (void)cancelAllHttpQueue;
{
    [_networkQueues makeObjectsPerformSelector:@selector(cancelAllOperations)];
}

- (void)cancelHttpRequest:(MFWHttpRequest*)request;
{
    if (!request)
    {
        return;
    }
    
    for ( ASINetworkQueue *networkQueue in _networkQueues)
    {
        NSArray *allWaitingRequest = [networkQueue operations];
        for (ASIHTTPRequest *asiRequest in allWaitingRequest)
        {
            MFWHttpRequest *mfwHttpRequest = [asiRequest.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];
            if (mfwHttpRequest == request)
            {
                [asiRequest clearDelegatesAndCancel];
                break;
            }
        }
    }
}

- (void)cancelTask:(MFWHttpTask*)task;
{
    if (!task)
    {
        return;
    }

    for ( ASINetworkQueue *networkQueue in _networkQueues)
    {
        NSArray *allWaitingRequest = [networkQueue operations];
        for (ASIHTTPRequest *asiRequest in allWaitingRequest)
        {
            MFWHttpRequest *mfwHttpRequest = [asiRequest.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];

            // TODO:
            if (mfwHttpRequest.task == task)
            {
                [asiRequest clearDelegatesAndCancel];
                break;
            }
        }
    }
}

- (void)cancelAllTaskBySender:(id)sender;
{
    if (!sender)
    {
        return;
    }
    
    for ( ASINetworkQueue *networkQueue in _networkQueues)
    {
        NSArray *waitingRequests = [networkQueue operations];
        
        for (ASIHTTPRequest *asiRequest in waitingRequests)
        {
            MFWHttpRequest *mfwRequest = [asiRequest.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];
            if ([mfwRequest.task.senders containsWeakObject:sender])
            {
                [mfwRequest.task.senders removeWeakObject:sender];
                
                if ([mfwRequest.task.senders count] == 0)
                {
                    [asiRequest clearDelegatesAndCancel];
                }
            }// end if
        }//~ end inner for-loop
    }//~ end outer for-foop
}

#pragma mark - Callback trigger
- (void)_notifyTaskAdded:(MFWHttpTask*)task
{
    [self performSelectorOnMainThread:@selector(_mainThreadNotifyTaskAdded:) withObject:task waitUntilDone:NO];
}

- (void)_mainThreadNotifyTaskAdded:(MFWHttpTask*)task
{
    if (task == nil)
    {
        return;
    }
    
    NSArray *observersOnCategory = [_weakDataObserverDict objectForKey:task.dataIdentifier.dataCategoryName];
    
    if (observersOnCategory)
    {
        for (WeakReference *weakRef in observersOnCategory)
        {
            id <MFWDataObserver> observer = weakRef.nonretainedObjectValue;
            if ([observer respondsToSelector:@selector(taskDidAddedToEngine:)])
            {
                [observer taskDidAddedToEngine:task];
            }
        }
    }
}
#pragma mark - ASIHTTPRequestDelegate methods
// args[0] -> MFWJson
// args[1] -> MFWHttpRequest
// args[2] -> ASIHttpRequest
- (void)mainThreadCallbackForGlobalRequestHook:(NSArray*)args
{
    MFWJson *jsonObj = [args objectAtIndex:0];
    MFWHttpRequest *mfwReqeust = [args objectAtIndex:1];
    ASIHTTPRequest *asiRequest = [args objectAtIndex:2];
    
    int len = [_weakGlobalResultHooks count];
    for (int i = 0 ; i < len; i++)
    {
        id<MFWGlobalHttpResultHook> hook = [_weakGlobalResultHooks weakObjectAtIndex:i];
        if ([hook respondsToSelector:@selector(globalJsonResultHook:forRequest:)])
        {
            [hook globalJsonResultHook:jsonObj forRequest:mfwReqeust];
        }
        else if ([hook respondsToSelector:@selector(globalRawResultHook:forRequest:)])
        {
            [hook globalRawResultHook:asiRequest.responseData forRequest:mfwReqeust];
        }
    }
}

// args[0] -> MFWHttpTask
- (void)mainThreadCallbackForTaskSuccess:(NSArray*)args
{
    if ([args count] > 0)
    {
        MFWHttpTask *task = [args objectAtIndex:0];
        
        if (![task isKindOfClass:[MFWHttpTask class]])
        {
            return;
        }
        NSMutableArray *observersOnDataCategory = [[_weakDataObserverDict objectForKey:task.dataIdentifier.dataCategoryName] mutableCopy];
        
        if (task.block)
        {
            task.block(YES,task.itemArrayData,nil);
        }
        
        for (WeakReference *weakRef in observersOnDataCategory)
        {
            id <MFWDataObserver> callback = weakRef.nonretainedObjectValue;
            
            if (callback)
            {
                
                if ([callback respondsToSelector:@selector(requestFinishedForTask:withModel:dataId:requestType:itemArray:success:error:)])
                {
                    [callback requestFinishedForTask:task withModel:task.relativeDataModel dataId:task.dataIdentifier.dataId requestType:task.dataRequestType itemArray:task.itemArrayData success:YES error:nil];
                }
                else if ([callback respondsToSelector:@selector(requestFinishedForTask:success:error:)])
                {
                    [callback requestFinishedForTask:task success:YES error:nil];
                }
            }
        }
    }
}

// args[0] -> MFWHttpTask
// args[1] -> NSError
- (void)mianThreadCallbackForTaskFail:(NSArray*)args
{
    if ([args count] > 1)
    {
        MFWHttpTask *task = [args objectAtIndex:0];
        NSError *error = [args objectAtIndex:1];
        
        DLog(@" > !!!! Error:%@", error);
        
        if (![task isKindOfClass:[MFWHttpTask class]])
        {
            return;
        }
        
        if (task.block)
        {
            task.block(NO,nil,[error description]);
        }
        
        NSMutableArray *observersOnDataCategory = [[_weakDataObserverDict objectForKey:task.dataIdentifier.dataCategoryName] mutableCopy];
        
        for (WeakReference *weakRef in observersOnDataCategory)
        {
            id <MFWDataObserver> callback = weakRef.nonretainedObjectValue;
            if ([callback respondsToSelector:@selector(requestFinishedForTask:withModel:dataId:requestType:itemArray:success:error:)])
            {
                [callback requestFinishedForTask:task withModel:task.relativeDataModel dataId:task.dataIdentifier.dataId requestType:task.dataRequestType itemArray:task.itemArrayData success:NO error:error];
            }
            else if (callback && [callback respondsToSelector:@selector(requestFinishedForTask:success:error:)])
            {
                [callback requestFinishedForTask:task success:NO error:error];
            }
        }
    }
}

- (void)threadReqeustSuccessed:(ASIHTTPRequest*)request
{
    MFWHttpRequest *mfwHttpRequest = [request.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];
    
    MFWJson *json = [MFWJson jsonWithData:request.responseData];
    
    if (json)
    {
        [self performSelectorOnMainThread:@selector(mainThreadCallbackForGlobalRequestHook:)
                               withObject:@[json, mfwHttpRequest, request]
                            waitUntilDone:NO];
    }
    MFWHttpTask *task = mfwHttpRequest.task;
    if (task)
    {
        MFWDataModel *dataProvider = [[MFWDataCenter sharedInstance] dataModelForCategory:task.dataIdentifier.dataCategoryName];
        
        NSAssert(dataProvider, @"threadRequestFinished >> dataProvider is nil");
        
        BOOL processeRes = NO;
        
        processeRes = [dataProvider processSuccRawData:request.responseData parsedJson:json forTask:task];
        
        if (processeRes)
        {//
            [self performSelectorOnMainThread:@selector(mainThreadCallbackForTaskSuccess:) withObject:@[task] waitUntilDone:NO];
        }
        else
        {// http request is success, but server service request is failed
            NSError *error = nil;
            
            if (!json)
            {// parse json error
                error = [NSError errorWithDomain:MFWErrorDomain code:kError_JsonParseFail userInfo:@{kMFWErrorInfo_Str_Msg : @"parse json error"}];
            }
            else
            {// retrieve error message
                //TDDO:
                NSString *errorMsg = @"Ask Who?";
                int errorCode = 10000;
                error = [NSError errorWithDomain:MFWErrorDomain
                                            code:errorCode
                                        userInfo:@{kMFWErrorInfo_Str_Msg : errorMsg,
                                             kMFWErrorInfo_Int_StatusCode: [NSNumber numberWithInt:request.responseStatusCode]}];
            }
            
            [self performSelectorOnMainThread:@selector(mianThreadCallbackForTaskFail:) withObject:@[task, error] waitUntilDone:NO];
        }
    }
    else if (mfwHttpRequest.requestCompleteCallback)
    {
        NSError *error = nil;
        
        if (!json)
        {// parse json error
            error = [NSError errorWithDomain:MFWErrorDomain code:kError_JsonParseFail userInfo:@{kMFWErrorInfo_Str_Msg : @"parse json error"}];
        }
        
        // call the result handler block on the main queue
        dispatch_async( dispatch_get_main_queue(), ^{
            mfwHttpRequest.requestCompleteCallback(mfwHttpRequest, request.responseData, json, error);
        });

    }
    else
    {
        DLog(@"!!!! No callback to notify RequestSuccessed");
    }
}

- (void)threadRequestFailed:(ASIHTTPRequest*)request
{
    MFWHttpRequest *mfwHttpRequest = [request.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];
    
    MFWJson *json = [MFWJson jsonWithData:request.responseData];
    
    NSString *errorMsg = @"网络错误";
    int errorCode = kError_UnknowError;
    NSError *asiError = [request error];
    if (asiError)
    {
        if (asiError.code == ASIRequestTimedOutErrorType)
        {
            errorCode = kError_NetworkTimeout;
            errorMsg = @"网络超时";
        }
    }
    
    NSError *error = [NSError errorWithDomain:MFWErrorDomain
                                code:errorCode
                            userInfo:@{kMFWErrorInfo_Str_Msg : errorMsg,
                                 kMFWErrorInfo_Int_StatusCode: [NSNumber numberWithInt:request.responseStatusCode]}];
    
    MFWHttpTask *task = mfwHttpRequest.task;
    if (task)
    {
        MFWDataModel *dataProvider = [[MFWDataCenter sharedInstance] dataModelForCategory:task.dataIdentifier.dataCategoryName];
        
        NSAssert(dataProvider, @"threadRequestFinished >> dataProvider is nil");
        
        [dataProvider processFailRawData:request.responseData parsedJson:json forTask:task];
        
        [self performSelectorOnMainThread:@selector(mianThreadCallbackForTaskFail:) withObject:@[task, error] waitUntilDone:NO];
    }
    else if (mfwHttpRequest.requestCompleteCallback)
    {
        // call the result handler block on the main queue
        dispatch_async( dispatch_get_main_queue(), ^{
            mfwHttpRequest.requestCompleteCallback(mfwHttpRequest, request.responseData, json, error);
        });
    }
    else
    {
        DLog(@"!!!! No callback to notify RequestFailed");
    }
}

- (void)asiRequestSuccessed:(ASIHTTPRequest *)request;
{
    int responseStatusCode = [request responseStatusCode];
    if (responseStatusCode == 200)
    {
        [self performSelectorInBackground:@selector(threadReqeustSuccessed:)  withObject:request];
    }
    else
    {
        [self performSelectorInBackground:@selector(threadRequestFailed:) withObject:request];
    }

}
- (void)asiRequestFailed:(ASIHTTPRequest *)request;
{
    [self performSelectorInBackground:@selector(threadRequestFailed:) withObject:request];
}

#pragma mark - ASIHttpRequest generator

- (ASIHTTPRequest*)asiNormalRequest:(MFWHttpRequest*)dataRequest withArgs:(NSDictionary*)signedArgs;
{
    NSURL *URL = nil;
    
    if (signedArgs)
    {
        __block NSMutableString *queryStr = [NSMutableString string];
        [signedArgs enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            [queryStr appendFormat:@"%@=%@&", [key urlEncode], [obj urlEncode]];
        }];
        
        // delete last char '&'
        if ([queryStr length] > 0)
        {
            [queryStr deleteCharactersInRange:NSMakeRange(queryStr.length - 1, 1)];
        }
        
        NSString *requestUrlStr = [NSString stringWithFormat:@"%@?%@", dataRequest.requestUrl, queryStr];
        URL = [NSURL URLWithString:requestUrlStr];
    }
    else
    {
        URL = [NSURL URLWithString:dataRequest.requestUrl];
    }

    ASIHTTPRequest *asiHttpRequest = [[ASIHTTPRequest alloc] initWithURL:URL];
    
    asiHttpRequest.requestMethod = dataRequest.httpMethodStr;

    //add http header info
    if (dataRequest.headerParams)
    {
        [asiHttpRequest setRequestHeaders:dataRequest.headerParams];
    }

    asiHttpRequest.timeOutSeconds = dataRequest.timeOutSeconds;
    asiHttpRequest.userInfo = @{kASIRequest_UserInfo_MFWRequst: dataRequest};

    return asiHttpRequest;
}

- (ASIHTTPRequest*)asiFormDataRequest:(MFWHttpRequest*)dataRequest withArgs:(NSDictionary*)signedArgs
{
    NSURL *URL = [NSURL URLWithString:dataRequest.requestUrl];
    ASIFormDataRequest *asiFormRequest = [[ASIFormDataRequest alloc] initWithURL:URL];
    
    asiFormRequest.requestMethod = dataRequest.httpMethodStr;
    
    if (signedArgs)
    {
        [signedArgs enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            [asiFormRequest addPostValue:obj forKey:key];
        }];
    }
    
    if (dataRequest.dataParams.count)
    {
        [dataRequest.dataParams enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            [asiFormRequest addData:obj forKey:key];
        }];
    }
    
    if (dataRequest.imageParams.count)
    {
        [dataRequest.imageParams enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            if ([obj isKindOfClass:[UIImage class]])
            {
                UIImage *image = obj;
                NSData *jpegeData = UIImageJPEGRepresentation(image, 0.7);
                [asiFormRequest addData:jpegeData withFileName:key
                         andContentType:@"image/jpeg" forKey:key];
            }
            else if ([obj isKindOfClass:[NSURL class]])
            {
                NSURL *filePath = (NSURL*)obj;
                if ([filePath isFileURL])
                {
                    NSString *fileName = [filePath lastPathComponent];
                    NSString *contentType = [NSString stringWithFormat:@"image/%@", fileName.pathExtension];
                    NSData *fileData = [NSData dataWithContentsOfURL:filePath];
                    [asiFormRequest addData:fileData withFileName:fileName andContentType:contentType forKey:key];
                }
            }
        }];
    }
    
    //add http header info
    if (dataRequest.headerParams)
    {
        [asiFormRequest setRequestHeaders:dataRequest.headerParams];
    }
    
    asiFormRequest.timeOutSeconds = dataRequest.timeOutSeconds;
    asiFormRequest.userInfo = @{kASIRequest_UserInfo_MFWRequst: dataRequest};

    return asiFormRequest;
}
#pragma mark - Memory warning
- (void)didReceiveMemoryWarning
{
    [self _checkAndCleanUnusedCategoryData];
}

#pragma mark - 取游客token



#pragma mark - Progress

- (void)asiRequestStarted:(ASIHTTPRequest *)request
{
    // 响应开始
    MFWHttpRequest *mfwHttpRequest = [request.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];
    MFWHttpRequestProgress *progress = mfwHttpRequest.progressData;
    progress.sentBytes = request.totalBytesSent;
    progress.sendTotalBytes = request.postLength;
    if (mfwHttpRequest.requestStartCallback) {
        mfwHttpRequest.requestStartCallback(mfwHttpRequest);
    }
}

- (void)asiRequest:(ASIHTTPRequest *)request didReceiveResponseHeaders:(NSDictionary *)responseHeaders
{
    if ((request.responseStatusCode / 100) == 2) {
        MFWHttpRequest *mfwHttpRequest = [request.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];
        MFWHttpRequestProgress *progress = mfwHttpRequest.progressData;
        progress.receivedBytes = MAX(request.partialDownloadSize,0);
        progress.receiveTotalBytes = MAX(request.contentLength,0) + progress.receivedBytes;
    }
}

/**
 - (void)request:(ASIHTTPRequest *)request willRedirectToURL:(NSURL *)newURL;
 - (void)requestRedirected:(ASIHTTPRequest *)request;
 */

#pragma mark - ASIProgressDelegate

// Called when the request receives some data - bytes is the length of that data
- (void)request:(ASIHTTPRequest *)request didReceiveBytes:(long long)bytes
{
    MFWHttpRequest *mfwHttpRequest = [request.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];
    MFWHttpRequestProgress *progress = mfwHttpRequest.progressData;
    progress.receivedBytes += MAX(bytes,0);
    
    if (mfwHttpRequest.requestDownloadProgressCallBack) {
        mfwHttpRequest.requestDownloadProgressCallBack(mfwHttpRequest, progress.receivedBytes, progress.receiveTotalBytes);
    }
    
    NSLog(@"receive: +%llu r%llu t%llu", bytes, progress.receivedBytes, progress.receiveTotalBytes);
}

// Called when the request sends some data
// The first 32KB (128KB on older platforms) of data sent is not included in this amount because of limitations with the CFNetwork API
// bytes may be less than zero if a request needs to remove upload progress (probably because the request needs to run again)
- (void)request:(ASIHTTPRequest *)request didSendBytes:(long long)bytes
{
    MFWHttpRequest *mfwHttpRequest = [request.userInfo objectForKey:kASIRequest_UserInfo_MFWRequst];
    MFWHttpRequestProgress *progress = mfwHttpRequest.progressData;
    progress.sentBytes = request.totalBytesSent;
    
    if (mfwHttpRequest.requestSendProgressCallback) {
        mfwHttpRequest.requestSendProgressCallback(mfwHttpRequest, progress.sentBytes, progress.sendTotalBytes);
    }
    
    NSLog(@"send: +%llu s%llu t%llu", bytes, progress.sentBytes, request.postLength);
}

@end
